package Meta

import (
	"errors"
	"fmt"
	"db2s/Dm"
	"db2s/Gr"
	"db2s/Ms"
	"db2s/My"
	"db2s/Or"
	"db2s/ref"
	"strings"
)

var (
	tableMeMap = map[string]any{
		"mysql":     My.TableMe{},
		"oracle":    Or.TableMe{},
		"cluster":   Gr.TableMe{},
		"sqlserver": Ms.TableMe{},
		"dm":        Dm.TableMe{},
	}
	columnMeMap = map[string]any{
		"mysql":     My.ColumnMe{},
		"oracle":    Or.ColumnMe{},
		"cluster":   Gr.ColumnMe{},
		"sqlserver": Ms.ColumnMe{},
		"dm":        Dm.ColumnMe{},
	}
	indexMeMap = map[string]any{
		"mysql":     My.IndexMe{},
		"oracle":    Or.IndexMe{},
		"cluster":   Gr.IndexMe{},
		"sqlserver": Ms.IndexMe{},
		"dm":        Dm.IndexMe{},
	}
	partitionMeMap = map[string]any{
		"mysql":     My.PartitionMe{},
		"oracle":    Or.PartitionMe{},
		"cluster":   Gr.PartitionMe{},
		"sqlserver": Ms.PartitionMe{},
		"dm":        Dm.PartitionMe{},
	}
	viewMeMap = map[string]any{
		"mysql":     &My.ViewMe{},
		"oracle":    &Or.ViewMe{},
		"cluster":   &Gr.ViewMe{},
		"sqlserver": &Ms.ViewMe{},
		"dm":        &Dm.ViewMe{},
	}
	mViewMeMap = map[string]any{
		"mysql":   &My.MViewMe{},
		"oracle":  &Or.MViewMe{},
		"cluster": &Gr.MViewMe{},
		"dm":      &Dm.MViewMe{},
	}
	seqMeMap = map[string]any{
		"mysql":     My.SeqMe{},
		"oracle":    Or.SeqMe{},
		"cluster":   Gr.SeqMe{},
		"sqlserver": Ms.SeqMe{},
		"dm":        Dm.SeqMe{},
	}
	jobMeMap = map[string]any{
		"mysql":   &My.JobMe{},
		"oracle":  &Or.JobMe{},
		"cluster": &Gr.JobMe{},
		//"sqlserver": &sqlserver.TableInfoMeta{},
		"dm": &Dm.JobMe{},
	}
	triggerMeMap = map[string]any{
		"mysql":   &My.TriggerMe{},
		"oracle":  &Or.TriggerMe{},
		"cluster": &Gr.TriggerMe{},
		//"sqlserver": &sqlserver.TableInfoMeta{},
		"dm": &Dm.TriggerMe{},
	}
	foreignMeMap = map[string]any{
		"mysql":   &My.ForeignMe{},
		"oracle":  &Or.ForeignMe{},
		"cluster": &Gr.ForeignMe{},
		//"sqlserver": &sqlserver.TableInfoMeta{},
		"dm": &Dm.ForeignMe{},
	}
	procedureMeMap = map[string]any{
		"mysql":   &My.ProcedureMe{},
		"oracle":  &Or.ProcedureMe{},
		"cluster": &Gr.ProcedureMe{},
		//"sqlserver": &sqlserver.TableInfoMeta{},
		"dm": &Dm.ProcedureMe{},
	}
	funcMeMap = map[string]any{
		"mysql":   &My.FuncMe{},
		"oracle":  &Or.FuncMe{},
		"cluster": &Gr.FuncMe{},
		//"sqlserver": &sqlserver.TableInfoMeta{},
		"dm": &Dm.FuncMe{},
	}
	typeMeMap = map[string]any{
		"mysql":   &My.TypeMe{},
		"oracle":  &Or.TypeMe{},
		"cluster": &Gr.TypeMe{},
		//"sqlserver": &sqlserver.TableInfoMeta{},
		"dm": &Dm.TypeMe{},
	}
	packageMeMap = map[string]any{
		"mysql":   &My.PackageMe{},
		"oracle":  &Or.PackageMe{},
		"cluster": &Gr.PackageMe{},
		//"sqlserver": &sqlserver.TableInfoMeta{},
		"dm": &Dm.PackageMe{},
	}
)

func NewMetaTable(drive string) (res TableEr, err error) {
	var e any
	if e, err = driveCheck(tableMeMap, drive); err != nil {
		err = ref.ErrAddPrintf("NewMetaTable", err)
		return
	}
	switch e.(type) {
	case My.TableMe:
		res = e.(My.TableMe)
	//case Dm.TableMe:
	//	res = e.(Dm.TableMe)
	//case Gr.TableMe:
	//	res = e.(Gr.TableMe)
	case Or.TableMe:
		res = e.(Or.TableMe)
	//case Ms.TableMe:
	//	res = e.(Ms.TableMe)
	default:
		return
	}
	return
}
func NewMetaColumn(drive string) (res ColumnEr, err error) {
	var e any
	if e, err = driveCheck(columnMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case My.ColumnMe:
		//p := e.(My.ColumnMe)
		//if err = ref.UpdateStructJsonValue(&p, "parameter", parameter); err != nil {
		//	return
		//}
		res = e.(My.ColumnMe)
	//case Dm.ColumnMe:
	//	//p := e.(Dm.ColumnMe)
	//	//if err = ref.UpdateStructJsonValue(&p, "parameter", parameter); err != nil {
	//	//	return
	//	//}
	//	res = e.(Dm.ColumnMe)
	//case Gr.ColumnMe:
	//	//p := e.(Gr.ColumnMe)
	//	//if err = ref.UpdateStructJsonValue(&p, "parameter", parameter); err != nil {
	//	//	return
	//	//}
	//	res = e.(Gr.ColumnMe)
	case Or.ColumnMe:
		res = e.(Or.ColumnMe)
	//case Ms.ColumnMe:
	//	res = e.(Ms.ColumnMe)
	default:
		return
	}
	return
}
func NewMetaIndexMe(drive string) (res IndexEr, err error) {
	var e any
	if e, err = driveCheck(indexMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case My.IndexMe:
		res = e.(My.IndexMe)
	//case Dm.IndexMe:
	//	//if err = ref.UpdateStructJsonValue(e.(*Dm.IndexMe), "parameter", parameter); err != nil {
	//	//	return
	//	//}
	//	res = e.(Dm.IndexMe)
	//case Gr.IndexMe:
	//	//if err = ref.UpdateStructJsonValue(e.(*Gr.IndexMe), "parameter", parameter); err != nil {
	//	//	return
	//	//}
	//	res = e.(Gr.IndexMe)
	case Or.IndexMe:
		//if err = ref.UpdateStructJsonValue(e.(*Or.IndexMe), "parameter", parameter); err != nil {
		//	return
		//}
		res = e.(Or.IndexMe)
	//case Ms.IndexMe:
	//	res = e.(Ms.IndexMe)
	default:
		return
	}
	return
}
func NewMetaPartition(drive string) (res PartitionEr, err error) {
	var e any
	if e, err = driveCheck(partitionMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case My.PartitionMe:
		//if err = ref.UpdateStructJsonValue(e.(*My.PartitionMe), "parameter", parameter); err != nil {
		//	return
		//}
		res = e.(My.PartitionMe)
	case Dm.PartitionMe:
		//if err = ref.UpdateStructJsonValue(e.(*Dm.PartitionMe), "parameter", parameter); err != nil {
		//	return
		//}
		//res = e.(Dm.PartitionMe)
	//case Gr.PartitionMe:
	//	//if err = ref.UpdateStructJsonValue(e.(*Gr.PartitionMe), "parameter", parameter); err != nil {
	//	//	return
	//	//}
	//	res = e.(Gr.PartitionMe)
	case Or.PartitionMe:
		res = e.(Or.PartitionMe)
	//case Ms.PartitionMe:
	//	res = e.(Ms.PartitionMe)
	default:
		return
	}
	return
}
func NewMetaView(drive string, parameter Parameter) (res ViewEr, err error) {
	var e any
	if e, err = driveCheck(viewMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case *My.ViewMe:
		if err = ref.UpdateStructJsonValue(e.(*My.ViewMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*My.ViewMe)
	case *Dm.ViewMe:
		if err = ref.UpdateStructJsonValue(e.(*Dm.ViewMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Dm.ViewMe)
	case *Gr.ViewMe:
		if err = ref.UpdateStructJsonValue(e.(*Gr.ViewMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Gr.ViewMe)
	case *Or.ViewMe:
		if err = ref.UpdateStructJsonValue(e.(*Or.ViewMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Or.ViewMe)
	//case *sqlserver.TableMe:
	//	res = e.(*sqlserver.TableMe)
	default:
		return
	}
	return
}
func NewMetaMView(drive string, parameter Parameter) (res ViewEr, err error) {
	var e any
	if e, err = driveCheck(mViewMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case *My.MViewMe:
		if err = ref.UpdateStructJsonValue(e.(*My.MViewMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*My.MViewMe)
	case *Dm.MViewMe:
		if err = ref.UpdateStructJsonValue(e.(*Dm.MViewMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Dm.MViewMe)
	case *Gr.MViewMe:
		if err = ref.UpdateStructJsonValue(e.(*Gr.MViewMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Gr.MViewMe)
	case *Or.MViewMe:
		if err = ref.UpdateStructJsonValue(e.(*Or.MViewMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Or.MViewMe)
	//case *sqlserver.TableMe:
	//	res = e.(*sqlserver.TableMe)
	default:
		return
	}
	return
}
func NewMetaSeq(drive string) (res SeqEr, err error) {
	var e any
	if e, err = driveCheck(seqMeMap, drive); err != nil {
		err = ref.ErrAddPrintf("NewMetaSeq", err)
		return
	}
	switch e.(type) {
	case My.SeqMe:
		res = e.(My.SeqMe)
	case Dm.SeqMe:
		res = e.(Dm.SeqMe)
	case Gr.SeqMe:
		res = e.(Gr.SeqMe)
	case Or.SeqMe:
		res = e.(Or.SeqMe)
	case Ms.SeqMe:
		res = e.(Ms.SeqMe)
	default:
		return
	}
	return
}
func NewMetaJob(drive string, parameter Parameter) (res JobEr, err error) {
	var e any
	if e, err = driveCheck(jobMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case *My.JobMe:
		if err = ref.UpdateStructJsonValue(e.(*My.JobMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*My.JobMe)
	case *Dm.JobMe:
		if err = ref.UpdateStructJsonValue(e.(*Dm.JobMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Dm.JobMe)
	case *Gr.JobMe:
		if err = ref.UpdateStructJsonValue(e.(*Gr.JobMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Gr.JobMe)
	case *Or.JobMe:
		if err = ref.UpdateStructJsonValue(e.(*Or.JobMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Or.JobMe)
	//case *sqlserver.TableMe:
	//	res = e.(*sqlserver.TableMe)
	default:
		return
	}
	return
}
func NewMetaTrigger(drive string, parameter Parameter) (res TriggerEr, err error) {
	var e any
	if e, err = driveCheck(triggerMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case *My.TriggerMe:
		if err = ref.UpdateStructJsonValue(e.(*My.TriggerMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*My.TriggerMe)
	case *Dm.TriggerMe:
		if err = ref.UpdateStructJsonValue(e.(*Dm.TriggerMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Dm.TriggerMe)
	case *Gr.TriggerMe:
		if err = ref.UpdateStructJsonValue(e.(*Gr.TriggerMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Gr.TriggerMe)
	case *Or.TriggerMe:
		if err = ref.UpdateStructJsonValue(e.(*Or.TriggerMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Or.TriggerMe)
	//case *sqlserver.TableMe:
	//	res = e.(*sqlserver.TableMe)
	default:
		return
	}
	return
}
func NewMetaForeign(drive string, parameter Parameter) (res ForeignEr, err error) {
	var e any
	if e, err = driveCheck(foreignMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case *My.ForeignMe:
		if err = ref.UpdateStructJsonValue(e.(*My.ForeignMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*My.ForeignMe)
	case *Dm.ForeignMe:
		if err = ref.UpdateStructJsonValue(e.(*Dm.ForeignMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Dm.ForeignMe)
	case *Gr.ForeignMe:
		if err = ref.UpdateStructJsonValue(e.(*Gr.ForeignMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Gr.ForeignMe)
	case *Or.ForeignMe:
		if err = ref.UpdateStructJsonValue(e.(*Or.ForeignMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Or.ForeignMe)
	//case *sqlserver.TableMe:
	//	res = e.(*sqlserver.TableMe)
	default:
		return
	}
	return
}
func NewMetaProcedure(drive string, parameter Parameter) (res ProcedureEr, err error) {
	var e any
	if e, err = driveCheck(procedureMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case *My.ProcedureMe:
		if err = ref.UpdateStructJsonValue(e.(*My.ProcedureMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*My.ProcedureMe)
	case *Dm.ProcedureMe:
		if err = ref.UpdateStructJsonValue(e.(*Dm.ProcedureMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Dm.ProcedureMe)
	case *Gr.ProcedureMe:
		if err = ref.UpdateStructJsonValue(e.(*Gr.ProcedureMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Gr.ProcedureMe)
	case *Or.ProcedureMe:
		if err = ref.UpdateStructJsonValue(e.(*Or.ProcedureMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Or.ProcedureMe)
	//case *sqlserver.TableMe:
	//	res = e.(*sqlserver.TableMe)
	default:
		return
	}
	return
}
func NewMetaFunc(drive string, parameter Parameter) (res FuncEr, err error) {
	var e any
	if e, err = driveCheck(funcMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case *My.FuncMe:
		if err = ref.UpdateStructJsonValue(e.(*My.FuncMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*My.FuncMe)
	case *Dm.FuncMe:
		if err = ref.UpdateStructJsonValue(e.(*Dm.FuncMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Dm.FuncMe)
	case *Gr.FuncMe:
		if err = ref.UpdateStructJsonValue(e.(*Gr.FuncMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Gr.FuncMe)
	case *Or.FuncMe:
		if err = ref.UpdateStructJsonValue(e.(*Or.FuncMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Or.FuncMe)
	//case *sqlserver.TableMe:
	//	res = e.(*sqlserver.TableMe)
	default:
		return
	}
	return
}
func NewMetaType(drive string, parameter Parameter) (res TypeEr, err error) {
	var e any
	if e, err = driveCheck(typeMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case *My.TypeMe:
		if err = ref.UpdateStructJsonValue(e.(*My.TypeMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*My.TypeMe)
	case *Dm.TypeMe:
		if err = ref.UpdateStructJsonValue(e.(*Dm.TypeMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Dm.TypeMe)
	case *Gr.TypeMe:
		if err = ref.UpdateStructJsonValue(e.(*Gr.TypeMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Gr.TypeMe)
	case *Or.TypeMe:
		if err = ref.UpdateStructJsonValue(e.(*Or.TypeMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Or.TypeMe)
	//case *sqlserver.TableMe:
	//	res = e.(*sqlserver.TableMe)
	default:
		return
	}
	return
}
func NewMetaPackage(drive string, parameter Parameter) (res PackageEr, err error) {
	var e any
	if e, err = driveCheck(packageMeMap, drive); err != nil {
		return
	}
	switch e.(type) {
	case *My.PackageMe:
		if err = ref.UpdateStructJsonValue(e.(*My.PackageMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*My.PackageMe)
	case *Dm.PackageMe:
		if err = ref.UpdateStructJsonValue(e.(*Dm.PackageMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Dm.PackageMe)
	case *Gr.PackageMe:
		if err = ref.UpdateStructJsonValue(e.(*Gr.PackageMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Gr.PackageMe)
	case *Or.PackageMe:
		if err = ref.UpdateStructJsonValue(e.(*Or.PackageMe), "parameter", parameter); err != nil {
			return
		}
		res = e.(*Or.PackageMe)
	//case *sqlserver.TableMe:
	//	res = e.(*sqlserver.TableMe)
	default:
		return
	}
	return
}
func driveCheck(driveMap map[string]any, name string) (e any, err error) {
	var ok bool
	e, ok = driveMap[name]
	if ok {
		return
	}
	err = errors.New(fmt.Sprintf("input database type error. input is {%v} What is expected is {%v}", name, func() string {
		var p []string
		for k := range driveMap {
			p = append(p, k)
		}
		return strings.Join(p, ",")
	}()))
	return
}
func joinResult(join string, obj []any) (res any) {
	switch join {
	case "left":
		return obj[0]
	case "right":
		if len(obj) == 1 {
			return obj[0]
		}
		return obj[1]
	case "join":
		return obj
	}
	return
}

func joinExec(join string, k int) bool {
	switch {
	case strings.EqualFold(join, "left") && k == 1:
		return true
	case strings.EqualFold(join, "right") && k == 0:
		return true
	}
	return false
}
